/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "ipmb.h"
#include <chrono>
#include <mc/log.h>
#include <thread>

namespace mc {

ipmb::ipmb(mc::engine::service* service) : m_service(service), m_retry_count(1) {
    if (!m_service) {
        MC_THROW(mc::invalid_argument_exception, "Service pointer cannot be null");
    }

    ilog("IPMB protocol library initialized: retry=${retry}", ("retry", m_retry_count));
}

ipmb::~ipmb() {
    ilog("IPMB protocol library destroyed");
}

std::optional<ipmi_response> ipmb::send_request(ipmb_channel_type channel_type, ipmi_net_fn net_fn,
                                                uint8_t cmd, const std::vector<uint8_t>& data,
                                                uint32_t timeout_ms) {
    ipmi_request request{.net_fn = net_fn, .cmd = cmd, .data = data, .timeout_ms = timeout_ms};

    return send_request(channel_type, request);
}

std::optional<ipmi_response> ipmb::send_request(ipmb_channel_type   channel_type,
                                                const ipmi_request& request) {
    ilog("Sending IPMI request: Channel=${channel}, NetFn=0x${net_fn:02x}, Cmd=0x${cmd:02x}, "
         "DataLen=${data_len}",
         ("channel", static_cast<int>(channel_type))("net_fn", static_cast<int>(request.net_fn))(
             "cmd", static_cast<int>(request.cmd))("data_len", request.data.size()));

    // 使用重试机制发送请求
    auto operation = [this, channel_type, &request]() -> std::optional<ipmi_response> {
        return send_to_ipmi_core(channel_type, request);
    };

    auto result = retry_operation<ipmi_response>(operation);

    if (result.has_value()) {
        ilog("IPMI request successful: CC=0x${cc:02x}",
             ("cc", static_cast<int>(result->completion_code)));
    } else {
        elog("IPMI request failed");
    }

    return result;
}

std::optional<ipmi_response> ipmb::send_to_ipmi_core(ipmb_channel_type   channel_type,
                                                     const ipmi_request& request) {
    if (!m_service) {
        elog("Invalid service pointer");
        return std::nullopt;
    }

    try {
        mc::dict empty_ctx;

        mc::variants args = {
            empty_ctx,                            // a{ss} - 上下文(传空字典)
            static_cast<uint8_t>(channel_type),   // channel_type (通道类型)
            DEFAULT_INSTANCE,                     // instance (实例序号)
            static_cast<uint8_t>(request.net_fn), // net_fn (网络功能码)
            DEFAULT_LUN,                          // lun (逻辑单元号)
            request.cmd,                          // cmd (命令字)
            request.data                          // payload (命令参数)
        };

        mc::variant result = m_service->timeout_call(
            mc::milliseconds(request.timeout_ms), std::string(IPMI_CORE_SERVICE),
            std::string(IPMI_CORE_PATH), std::string(IPMI_CORE_INTERFACE), "Request",
            "a{ss}yyyyyay", args);

        // 解析响应 - IPMI Request 方法返回两个值：completion_code 和 data
        if (result.is_array()) {
            auto result_array = result.as_array();
            if (result_array.size() >= 2) {
                // 第一个返回值是完成码
                auto completion_code = result_array[0];
                // 第二个返回值是数据
                auto response_data = result_array[1];

                return parse_response(completion_code, response_data);
            } else {
                elog("ipmi_core returned insufficient result values: expected 2, got ${count}",
                     ("count", result_array.size()));
                return std::nullopt;
            }
        }

        elog("ipmi_core returned invalid result type: expected array, got ${type}",
             ("type", result.get_type_name()));
        return std::nullopt;

    } catch (const std::exception& e) {
        elog("Exception calling ipmi_core: ${error}", ("error", e.what()));
        return std::nullopt;
    } catch (...) {
        // 捕获所有其他异常，包括未知异常
        elog("Unknown exception occurred while calling ipmi_core");
        return std::nullopt;
    }
}

std::optional<ipmi_response> ipmb::parse_response(const mc::variant& completion_code,
                                                  const mc::variant& response_data) {
    try {
        ipmi_response response;

        // 解析完成码
        if (completion_code.is_integer()) {
            response.completion_code =
                static_cast<ipmi_completion_code>(completion_code.as<int64_t>());
        } else {
            elog("Invalid completion code type: expected integer, got ${type}",
                 ("type", completion_code.get_type_name()));
            return std::nullopt;
        }

        // 解析响应数据
        if (response_data.is_array()) {
            auto data_array = response_data.as_array();
            response.data.reserve(data_array.size());
            for (const auto& item : data_array) {
                if (item.is_integer()) {
                    response.data.push_back(static_cast<uint8_t>(item.as<int64_t>()));
                } else {
                    elog("Invalid data item type: expected integer, got ${type}",
                         ("type", item.get_type_name()));
                    return std::nullopt;
                }
            }
        } else if (response_data.is_string()) {
            // 如果数据是字符串，转换为字节数组
            std::string data_str = response_data.as_string();
            response.data.assign(data_str.begin(), data_str.end());
        } else {
            elog("Invalid response data type: expected array or string, got ${type}",
                 ("type", response_data.get_type_name()));
            return std::nullopt;
        }

        dlog("Parsed IPMI response: CC=0x${cc:02x}, DataLen=${data_len}",
             ("cc", static_cast<int>(response.completion_code))("data_len", response.data.size()));

        return response;

    } catch (const std::exception& e) {
        elog("Exception parsing response: ${error}", ("error", e.what()));
        return std::nullopt;
    }
}

template <typename T, typename Operation>
std::optional<T> ipmb::retry_operation(Operation operation) {
    std::optional<T> result;

    for (int attempt = 0; attempt <= m_retry_count; ++attempt) {
        try {
            result = operation();

            if (result.has_value()) {
                if (attempt > 0) {
                    ilog("Retry operation successful: retry count=${retry}", ("retry", attempt));
                }
                return result;
            }

        } catch (const std::exception& e) {
            wlog("Retry operation exception: ${error}, retry count=${retry}",
                 ("error", e.what())("retry", attempt));
        }

        // 如果不是最后一次尝试，则等待后重试
        if (attempt < m_retry_count) {
            auto delay_ms = 100 * (attempt + 1); // 递增延迟
            std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
        }
    }

    elog("Retry operation failed: maximum retry count reached ${max_retry}",
         ("max_retry", m_retry_count));
    return std::nullopt;
}

void ipmb::set_retry_count(int retry_count) {
    m_retry_count = std::max(0, retry_count); // 确保非负
    ilog("Retry count set: ${retry}", ("retry", m_retry_count));
}

int ipmb::get_retry_count() const {
    return m_retry_count;
}

namespace ipmi_utils {

std::string_view format_completion_code(ipmi_completion_code code) {
    switch (code) {
    case ipmi_completion_code::success:
        return "Success";
    case ipmi_completion_code::node_busy:
        return "Node Busy";
    case ipmi_completion_code::invalid_command:
        return "Invalid Command";
    case ipmi_completion_code::invalid_command_for_lun:
        return "Invalid Command for LUN";
    case ipmi_completion_code::timeout:
        return "Timeout";
    case ipmi_completion_code::out_of_space:
        return "Out of Space";
    case ipmi_completion_code::invalid_reservation:
        return "Invalid Reservation";
    case ipmi_completion_code::data_truncated:
        return "Data Truncated";
    case ipmi_completion_code::invalid_length:
        return "Invalid Length";
    case ipmi_completion_code::length_exceeded:
        return "Length Exceeded";
    case ipmi_completion_code::parameter_out_of_range:
        return "Parameter Out of Range";
    case ipmi_completion_code::cannot_return_data:
        return "Cannot Return Data";
    case ipmi_completion_code::req_data_not_present:
        return "Request Data Not Present";
    case ipmi_completion_code::invalid_data_field:
        return "Invalid Data Field";
    case ipmi_completion_code::command_illegal:
        return "Command Illegal";
    case ipmi_completion_code::response_not_provided:
        return "Response Not Provided";
    case ipmi_completion_code::duplicated_request:
        return "Duplicated Request";
    case ipmi_completion_code::unknown_error:
        return "Unknown Error";
    default:
        return "Unknown Completion Code";
    }
}

} // namespace ipmi_utils

} // namespace mc
